/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.tchepannou.limbe.mail;

import com.tchepannou.limbe.Context;
import com.tchepannou.limbe.helper.ClassHelper;

import javax.mail.MessagingException;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

/**
 *
 * @author herve
 */
public class MailControllerContainer
{
    //-- Static
    private static final Logger LOG = Logger.getLogger (MailControllerContainer.class.getName ());
    
    //-- Attribute
    private Context _context;
    private boolean _initialized;
    private Map<String, MailControllerWrapper> _wrappers = new HashMap<String, MailControllerWrapper> ();
    
    
    //-- Constructeur
    public MailControllerContainer ()
    {
    }


    //-- Public method
    public void init (Context context)
    {
        LOG.info ("Initializing");
        _context = context;
        try
        {
            loadMailControllers (_context.getBasePackage () + ".mail");
            for (MailControllerWrapper wrapper : _wrappers.values ())
            {
                LOG.info ("Initializing " + wrapper);
                wrapper.init (context);
            }

            _initialized = true;
            LOG.info ("Initialized");
        }
        catch (ClassNotFoundException e)
        {
            throw new IllegalStateException ("Unable to initialized the MessagingControllerContainer", e);
        }
        catch (IOException e)
        {
            throw new IllegalStateException ("Unable to initialized the MessagingControllerContainer", e);
        }
    }

    public void destroy ()
    {
        LOG.info ("Destroying");

        for (MailControllerWrapper wrapper : _wrappers.values ())
        {
            LOG.info ("Destroying " + wrapper);
            wrapper.destroy ();
        }

        _context = null;
        _wrappers.clear ();
        _initialized = false;

        LOG.info ("Destroyed");
    }

    public boolean isInitialized ()
    {
        return _initialized;
    }


    public MailController deliver (String path, Serializable data)
        throws MessagingException, IOException
    {
        MailControllerWrapper wrapper = getMailControllerWrapper (path);
        if (wrapper != null)
        {
            return wrapper.deliver (path, data);
        }
        else
        {
            throw new IllegalStateException ("No controller associated with " + path);
        }
    }

    public MailControllerWrapper register (Class<? extends MailController> clazz)
    {
        MailControllerWrapper wrapper = new MailControllerWrapper ();
        wrapper.setControllerClass (clazz);

        LOG.info ("Registering " + wrapper);
        _wrappers.put (wrapper.getName ().toLowerCase (), wrapper);

        return wrapper;
    }


    public MailControllerWrapper getMailControllerWrapper (String path)
    {
        String topic = extractNameFromPath (path);
        return _wrappers.get (topic.toLowerCase ());
    }




    //-- Private
    private String extractNameFromPath (String path)
    {
        String topic;
        String xpath = path != null ? path.toLowerCase () : "";
        int i = xpath.lastIndexOf ("/");
        if ( i == 0 )
        {
            topic = xpath.substring (1);
        }
        else
        {
            String xuri = xpath.substring (0, i);
            int j = xuri.lastIndexOf ("/");
            topic = j == 0
                ? xuri.substring (1)
                : xuri.substring (j + 1);
        }
        return topic;
    }

    private void loadMailControllers (String packageName)
        throws ClassNotFoundException,
               IOException
    {
        LOG.info("Loading Action classes from package: " + packageName);
        
        ClassHelper.Filter filter = createClassFilter(packageName);
        List<Class<?>> classes = ClassHelper.loadClasses(_context.getServletContext(), filter);
        LOG.info (classes.size() + " classes(s) loaded");
        
        for (Class<?> clazz : classes)
        {
            register ((Class<MailController>)clazz);
        }
    }
    
    
    private ClassHelper.Filter createClassFilter (final String packageName)
    {
        return new ClassHelper.Filter()
        {
            @Override
            public boolean accept(Class<?> clazz)
            {
                int modifier = clazz.getModifiers ();
                if (Modifier.isPublic (modifier) 
                    && !Modifier.isAbstract (modifier)
                    && clazz.getPackage ().getName ().startsWith (packageName) 
                    && MailController.class.isAssignableFrom (clazz) )
                {
                    return true;
                }
                return false;
            }
        };
    }
    
}
